#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>

#include "audio.h"
#include "analize.h"
#include "compare.h"
#include "udbus.h"

double main_threshold=0.5;
audio_size_t main_bufflen=4*1024;
audio_size_t main_channel=2;
const char default_device[]="hw"/*"/dev/dsp"*/;
const char* main_device=default_device;

audio_handle audio;
analize_handle analize;

/*
    _   _   _   _   _
  _| |_| |_| |_| |_| |_|
  
 */

void
main_set_busy(){
  static time_t t = 0;
  time_t c = time(NULL);
  if(c - t >= 4) {
    //idle_notifier_set_state("busy");
    t = c;
  }
}

int
main_activate(){
  //if(!audio_push_scenario("voip-handset")) return -1;
  if((audio = audio_open(main_device)) == NULL) return 0;
  
  if(main_channel == 0) main_channel = 1;
  if(main_channel > audio_get_nchannels(audio)) main_channel = audio_get_nchannels(audio) - 1;
  
  analize_setrate(analize, audio_get_framerate(audio));
}

int
main_deactive(){
  audio_close(audio);
  //audio_pull_scenario();
}

char
main_produce(double* curr_level,
	     double* curr_freq,
	     double* note_freq,
	     const char** note_name,
	     unsigned char* note_octa,
	     double* delta){
  CompareInfo compinfo;
  
  int16_t buffer[audio_get_nchannels(audio) * main_bufflen]; // for interleaved
  //int16_t buffer[audio_get_nchannels(audio)][main_bufflen]; // for non-interleaved
  
  main_set_busy();
  
  if(audio_read_data(audio, buffer, main_bufflen) == main_bufflen){
    if(0){
      u_int16_t i;
      for(i=0; i<main_bufflen; i+=8){
	fprintf(stderr, "%8d %8d %8d %8d    %8d %8d %8d %8d\n",
		buffer[i+0], buffer[i+1], buffer[i+2], buffer[i+3],
		buffer[i+4], buffer[i+5], buffer[i+6], buffer[i+7]);
      }
    }
    
    main_channel = abs(buffer[0]) > abs(buffer[1]) ? 1 : 2; // temporary shit >:-E
    
    analize_filldata(analize, buffer, main_channel - 1, audio_get_nchannels(audio)); // for interleaved
    //analize_filldata(analize, buffer, (main_channel - 1) * main_bufflen, 1); // for non-interleaved
    //analize_filldata(analize, buffer[main_channel - 1], 0, 1); // for non-interleaved
    
    *curr_level = (log(analize_average(analize))+10)*0.1;
    
    if(*curr_level < main_threshold) return 0;
    
    analize_execute(analize);
    
    compare_detect(analize_frequency(analize), &compinfo);
    
    *curr_freq = analize_frequency(analize);
    
    *note_freq = compinfo.curr.freq;
    *note_name = compinfo.curr.name;
    *note_octa = compinfo.curr.octa;
    
    { double diff;
      diff = (*curr_freq - *note_freq);
      diff /= diff > 0 ? (compinfo.next.freq - *note_freq) : (*note_freq - compinfo.prev.freq);
      diff *= 100.0;
      *delta = diff;
    }
    
    return 1;
  }
  
  return -1;
}

#define ERROR(msg) { fprintf(stderr, "ERROR: " msg "!\n"); exit(1); }

int main(int argc, char *argv[]) {
  char frontend='e';
  int argi;
  
  printf(NAME " is a guitar tuner software for OpenMoKo-based mobile communicators such as Neo.\n(L) Kayo, 2009.\n");
  for(argi=1; argi<argc; argi++){
    if(!strcmp(argv[argi], "-h") || !strcmp(argv[argi], "--help")) { // help and usage
      printf("Usage: k11gTun [options]\n"
	     "Available options:\n"
	     " -f|--frontend <c|cli|e|efl> // c|cli - command line, e|efl - enlightened gui; default: -f %c\n"
	     " -d|--device <audio device> // default: -d %s\n"
	     " -c|--channel <audio channel> // default: -c %d\n"
	     " -b|--bufflen <length of audio buffer>[<K>] // default: -b %d\n"
	     " -t|--threshold <threshold value> // default: -t %f\n", //[<M|K>]
	     frontend, main_device, main_channel, main_bufflen, main_threshold);
      exit(0);
    }
    if(!strcmp(argv[argi], "-f") || !strcmp(argv[argi], "--frontend")) { // frontend
      argi++;
      if(argi >= argc || strlen(argv[argi]) < 1) ERROR("Expected frontend specifier");
      if(!strcmp(argv[argi], "e") || !strcmp(argv[argi], "efl")) frontend='e';
      else if(!strcmp(argv[argi], "c") || !strcmp(argv[argi], "cli")) frontend='c';
      else ERROR("Unexpected frontend");
    }
    if(!strcmp(argv[argi], "-d") || !strcmp(argv[argi], "--device")) { // device
      argi++;
      if(argi >= argc || strlen(argv[argi]) < 1) ERROR("Expected device specifier");
      main_device=argv[argi];
    }
    if(!strcmp(argv[argi], "-b") || !strcmp(argv[argi], "--bufflen")) { // audio buffer length
      unsigned int coef=1;
      argi++;
      if(strlen(argv[argi])>1 && argv[argi][strlen(argv[argi])-1]=='K'){
	coef=1024;
	argv[argi][strlen(argv[argi])-1]='\0';
      }
      if(argi >= argc || strlen(argv[argi])<1) ERROR("Expected length of the audio buffer");
      { const char digits[]="1234567890"; unsigned int i,c;
	for(i=0;i<strlen(argv[argi]);i++){ char p=0;
	  for(c=0;c<strlen(digits);c++) if(argv[argi][i]==digits[c]) p=1;
	  if(!p) ERROR("Incorrect length of the audio buffer"); } }
      sscanf(argv[argi], "%d", &main_bufflen);
      main_bufflen*=coef;
    }
    if(!strcmp(argv[argi], "-t") || !strcmp(argv[argi], "--threshold")) { // operation threshold
      //unsigned int thrd=0;
      //unsigned int coef=1;
      argi++;
      /*
      if(strlen(argv[argi])>1){
	if(argv[argi][strlen(argv[argi])-1]=='K'){
	  coef=1000;
	  argv[argi][strlen(argv[argi])-1]='\0';
	} else if(argv[argi][strlen(argv[argi])-1]=='M'){
	  coef=1000000;
	  argv[argi][strlen(argv[argi])-1]='\0';
	}
      }
      */
      if(argi >= argc || strlen(argv[argi])<1) ERROR("Expected threshold value");
      { const char digits[]="1234567890."; unsigned int i,c;
	for(i=0;i<strlen(argv[argi]);i++){ char p=0;
	  for(c=0;c<strlen(digits);c++) if(argv[argi][i]==digits[c]) p=1;
	  if(!p) ERROR("Incorrect threshold value"); } }
      //sscanf(argv[argi], "%f", &thrd);
      //main_threshold=thrd*coef;
      main_threshold=atof(argv[argi]);
    }
    if(!strcmp(argv[argi], "-c") || !strcmp(argv[argi], "--channel")) { // channel
      argi++;
      if(argi >= argc || strlen(argv[argi])<1) ERROR("Expected channel specifier");
      { const char digits[]="1234567890"; unsigned int i,c;
	for(i=0;i<strlen(argv[argi]);i++){ char p=0;
	  for(c=0;c<strlen(digits);c++) if(argv[argi][i]==digits[c]) p=1;
	  if(!p) ERROR("Incorrect channel specifier"); } }
      main_channel=atoi(argv[argi]);
    }
  }
  
  printf("Configuration: device=%s channel=%d bufflen=%d threshold=%f frountend=%c\n", main_device, main_channel, main_bufflen, main_threshold, frontend);
  
  udbus_init();
  
  analize = analize_open(main_bufflen);
  
  if(frontend=='c') cli_main();
  else if(frontend=='e') gui_main();
  
  analize_close(analize);
  
  udbus_finalize();
}

